// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include <sstream>

#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_names.h>
#include <google/protobuf/compiler/csharp/csharp_options.h>
#include <google/protobuf/compiler/csharp/csharp_reflection_class.h>
#include <google/protobuf/compiler/csharp_shioho/shioho_paser.h>
#include <google/protobuf/stubs/scoped_ptr.h>

#include <google/protobuf/compiler/csharp_shioho/shioho_msg.cc>

using namespace std;
namespace gpcc = google::protobuf::compiler::csharp;
using google::protobuf::internal::scoped_ptr;

extern vector<MsgInfo> vec;
extern string shioho_namespace;

namespace google
{
	namespace protobuf
	{
		namespace compiler
		{
			namespace shioho_paser
			{
				void AddPaserInfo(io::Printer *printer, string className, int protoId, string extendClassName)
				{
					printer->Print("public class $class_name$MsgPaser : $extend_class_name$<$class_name$>\n", "class_name", className, "extend_class_name", extendClassName);
					printer->Print("{\n");
					printer->Indent();
					printer->Print("protected override MessageParser<$class_name$> _msgPaser => $class_name$.Parser;\n", "class_name", className);
					printer->Print("protected override int _protoId => $proto_id$;\n", "proto_id", std::to_string(protoId));
					printer->Outdent();
					printer->Print("}\n");
				}

				void GenerateFile(const google::protobuf::FileDescriptor *file,
								  io::Printer *printer,
								  const gpcc::Options *options,
								  const ShiohoOption *opt, vector<MsgInfo>::iterator msgInfo)
				{

					printer->Print(
						"// \n"
						"//			Generated by the Shioho Msg Compiler.\n"
						"// \n"
						"//			   DO NOT EDIT!  DO NOT EDIT!\n"
						"// \n");

					// using
					printer->Print("using Google.Protobuf;\n");
					printer->Print("using Shioho.Net;\n");

					// namespace
					printer->Print("namespace $namespace_name$\n", "namespace_name", shioho_namespace);
					printer->Print("{\n");
					printer->Indent();

					// class
					string className;
					if (msgInfo->isPush)
					{
						className = msgInfo->className + "Push";
						AddPaserInfo(printer, className, msgInfo->protocolID, "AProtoMsgPaser");
					}
					else
					{
						className = msgInfo->className + "Request";
						AddPaserInfo(printer, className, msgInfo->protocolID, "AProtoMsgPaser");
						className = msgInfo->className + "Response";
						AddPaserInfo(printer, className, msgInfo->protocolID, "AProtoResponseMsgPaser");
					}

					printer->Outdent();
					printer->Print("}\n");
				}

				bool Generator::Generate(
					const FileDescriptor *file,
					const string &parameter,
					GeneratorContext *generator_context,
					string *error) const
				{

					vector<pair<string, string>> options;
					ParseGeneratorParameter(parameter, &options);

					struct gpcc::Options cli_options;
					ShiohoOption opt;

					for (int i = 0; i < options.size(); i++)
					{
						if (options[i].first == "file_extension")
						{
							cli_options.file_extension = options[i].second;
						}
						else if (options[i].first == "base_namespace")
						{
							cli_options.base_namespace = options[i].second;
							cli_options.base_namespace_specified = true;
						}
						else if (options[i].first == "internal_access")
						{
							cli_options.internal_access = true;
						}
						else
						{
							*error = "Unknown generator option: " + options[i].first;
							return false;
						}
					}

					vector<MsgInfo>::iterator msgInfo;
					msgInfo = vec.begin();
					while (msgInfo != vec.end())
					{
						string filename = msgInfo->className + "MsgPaser.cs";
						scoped_ptr<io::ZeroCopyOutputStream> output(
							generator_context->Open(filename));
						io::Printer printer(output.get(), '$');
						GenerateFile(file, &printer, &cli_options, &opt, msgInfo);

						cout << "[MsgController Generated Successfully]" << filename << endl;

						msgInfo++;
					}
				}
			} // namespace shioho_provider
		}	  // namespace compiler
	}		  // namespace protobuf
} // namespace google
